perf: improve Java LSP tool result handoff#1031
Conversation
Return structured file and line fields from findSymbol so the model can consume precise ranges without repeating symbol search. Accept line-suffixed Java locations in file structure resolution and document outlineInput handoff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the Java LSP Copilot tools’ result “handoff” so downstream steps (e.g., read_file / lsp_java_getFileStructure) can consume lsp_java_findSymbol output more directly, and so file resolution is more tolerant of Java-style path:line inputs and multi-root workspaces.
Changes:
- Enhanced
lsp_java_findSymbolpayload withfile,startLine,endLine, andoutlineInput, and added anextStephint to reduce unnecessary repeat tool calls. - Made file URI resolution tolerate Java location suffixes (e.g.,
src/Foo.java:42) and improved multi-root relative-path handling when the workspace folder name is included. - Updated skill docs, instrument instructions, and tool metadata to prefer
outlineInput/filehandoff patterns.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/copilot/tools/javaContextTools.ts | Adds Java path:line tolerance, improves multi-root relative resolution, and enriches findSymbol results with additional fields and guidance. |
| resources/skills/java-lsp-tools/SKILL.md | Documents the updated findSymbol output fields and the preferred handoff flow using outlineInput/file. |
| resources/instruments/javaLspContext.instructions.md | Updates Java chat instructions to prefer read_file with file/range and getFileStructure with outlineInput. |
| package.json | Updates tool metadata/schema descriptions to reflect new fields and tolerated path:line inputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Use normalized relative paths consistently when resolving file structure inputs and classify truncated findSymbol responses before small exact-result guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the PR focused on the evidence-backed findSymbol handoff contract. Do not claim getFileStructure accepts path:line inputs without telemetry proving that case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep findSymbol results as structured location data and leave next-tool choice to the model. The payload still exposes file, line, range, and outlineInput for downstream consumption without embedding result-level routing guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the redundant outlineInput field and keep findSymbol output focused on factual location data: file, startLine, endLine, and range. getFileStructure can consume the returned file when broader file outline context is needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/copilot/tools/javaContextTools.ts:344
- The PR description says
lsp_java_findSymbolwill add anoutlineInputfield and a top-levelnextStephint, but the tool output here only includesfile/startLine/endLine/readFileInputand returns{ results, total }. If those fields are required for handoff, they should be included in the actual payload.
const results = symbols.slice(0, limit).map(s => {
const file = vscode.workspace.asRelativePath(s.location.uri);
const { startLine, endLine } = toInclusiveLineRange(s.location.range);
return {
name: s.name,
kind: vscode.SymbolKind[s.kind],
container: s.containerName || undefined,
file,
startLine,
endLine,
readFileInput: toReadFileInput(file, startLine, endLine),
location: `${file}:${startLine}`,
range: `L${startLine}-${endLine}`,
};
});
resultCount = results.length;
const findSymbolPayload = { results, total: symbols.length };
responseCharCount = getResponseCharCount(findSymbolPayload);
return toResult(findSymbolPayload);
| let uri: vscode.Uri; | ||
| const normalizedInput = input.trim(); | ||
|
|
||
| if (input.includes("://")) { | ||
| if (normalizedInput.includes("://")) { | ||
| // URI string (e.g. "file:///home/user/project/src/Main.java") | ||
| uri = vscode.Uri.parse(input); | ||
| uri = vscode.Uri.parse(normalizedInput); |
There was a problem hiding this comment.
Addressed in 467198e by removing the ambiguous location: file:line field from lsp_java_findSymbol; the intended lsp_java_getFileStructure handoff now uses the structured file field instead.
| **findSymbol → getFileStructure → read_file (specific lines only)** | ||
|
|
||
| If `findSymbol` returns relevant symbols, move forward to `getFileStructure` or `read_file`; do not call `findSymbol` again with the same or similar identifier. | ||
| If `findSymbol` returns relevant symbols and source is needed, call `read_file` with the returned `readFileInput`, or call `getFileStructure` with the returned `file` when broader file context is needed. | ||
|
|
There was a problem hiding this comment.
Addressed in 467198e by using the full lsp_java_getFileStructure tool name in the workflow example and guidance.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
This draft updates the Java LSP Copilot tool contract so successful symbol and file-outline results are easier to consume by downstream tools, especially VS Code Copilot's built-in
read_file.Recent telemetry analysis showed that raw
findSymbol -> findSymbolis not a reliable problem metric by itself because most adjacent pairs are likely same-round/batch tool calls. The PR therefore avoids prescriptive next-step hints and focuses on factual, read-file-compatible result fields.Changes
lsp_java_findSymbolnow returns structured location fields plus a directread_fileadapter:filestartLineendLinerangereadFileInput: { filePath, offset, limit }location: path:linefield fromfindSymbol;fileis the canonical handoff field forlsp_java_getFileStructure.lsp_java_getFileStructurenow returns a top-levelfileand per-symbol read ranges:startLineendLinerangereadFileRange: { offset, limit }limittolsp_java_getFileStructureto control payload size:2060truncated: truewhen cappedasRelativePathincludes the workspace folder name.Expected experiment signal
This PR is not expected to eliminate all
findSymbol -> findSymboltransitions. Same-round/batch symbol lookups can still be valid.Expected improvements:
findSymbol -> readFilewhen source is needed.findSymbol -> readFile/getFileStructurewithin the next few tool calls.findSymbol -> findSymbolrepeat rate, especially pairs withdelta > 1sordelta > 5s.findSymbolresults.getFileStructurepayloads while preservinggetFileStructure -> readFileconsumption.Metrics that should not be treated as primary success criteria:
findSymbol -> findSymbol, because it is inflated by batch/parallel tool calls.findSymbolto always be followed byread_file;lsp_java_getFileStructure, additional symbol lookup, or generic search can still be valid depending on the task.Intentional non-goals
nextStepfield in tool results; next-tool selection remains with the model.outlineInput;fileis the single path handoff field.path:lineparsing inlsp_java_getFileStructure;findSymbolno longer returns apath:linefield that would encourage this misuse.Validation
npm run compilenpm run tslintnpm testcurrently fails in existing Library Controller tests becausetoReferencedLibraryPath/toReferencedLibraryExcludePathare not exported fromextension_bundle_1; unrelated to this LSP tool change.